"""问答服务：整合检索与生成，处理核心问答逻辑"""
from typing import List, Dict
import logging
from src.vector_db.chroma_db import ChromaDB
from src.embedding.huggingface_embedding import HuggingFaceEmbedding
from src.llm.llm_factory import LLMFactory  # 复用现有LLM工厂
from config.app_config import get_app_config

# 配置更详细的日志（包含函数名和关键变量）
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)  # 确保日志级别足够低，能输出信息


class QAService:
    def __init__(self):
        self.config = get_app_config()
        # 初始化嵌入模型（与文档存储一致）
        self.embedding = HuggingFaceEmbedding(
            model_name=self.config.embedding_config.model_name,
            model_path=self.config.embedding_config.model_path
        )
        logger.info(f"嵌入模型初始化完成：{self.config.embedding_config.model_name}")

        # 初始化向量库（用于检索）
        self.vector_db = ChromaDB(
            embedding=self.embedding,
            db_path=self.config.vector_db_path,
            collection_name=self.config.collection_name
        )
        logger.info(f"向量库初始化完成：{self.config.vector_db_path}（集合：{self.config.collection_name}）")

        # 初始化LLM（复用现有LLM工厂）
        self.llm = LLMFactory.get_llm()
        logger.info(f"LLM初始化完成：{type(self.llm).__name__}（模型类型：{self.config.llm_config.model_type}）")

    def _retrieve_relevant_docs(self, question: str, top_k: int) -> List[str]:
        """从向量库检索相关文档片段"""
        try:
            logger.info(f"开始检索相关文档：问题={question[:50]}...，top_k={top_k}")
            relevant_docs = self.vector_db.similarity_search(query=question, k=top_k)

            # 验证检索结果格式
            if not relevant_docs:
                logger.warning("未检索到任何相关文档")
                return []
            if not hasattr(relevant_docs[0], "page_content"):
                raise ValueError("检索到的文档对象缺少page_content属性")

            logger.info(f"检索完成，共获取{len(relevant_docs)}条文档")
            return [doc.page_content for doc in relevant_docs]
        except Exception as e:
            logger.error(f"文档检索失败: {str(e)}", exc_info=True)
            raise RuntimeError(f"检索相关文档失败: {str(e)}")

    def _generate_answer(self, question: str, related_docs: List[str]) -> str:
        """基于检索到的文档生成回答"""
        # 构建提示词（结合文档和问题）
        prompt = f"""
        请严格根据以下文档内容回答问题，不要添加文档外的信息。
        若文档中没有相关内容，直接回复"未找到相关信息"。

        文档内容：
        {'\n\n'.join(related_docs)}

        问题：{question}
        回答：
        """
        try:
            logger.info(f"开始生成回答：提示词长度={len(prompt)}字符")
            # 调用LLM生成回答（复用现有LLM的生成逻辑）
            response = self.llm.generate([prompt])

            # 关键修复：适配LLM返回的字典类型结果（而非对象）
            # 验证LLM返回格式
            if "generations" not in response:  # 检查字典中是否存在generations键
                raise ValueError(f"LLM返回结果缺少generations键，原始返回：{str(response)}")
            if not response["generations"] or not isinstance(response["generations"][0], list):
                raise ValueError(f"LLM返回结果的generations结构不正确，原始返回：{str(response)}")
            if "text" not in response["generations"][0][0]:  # 检查text键是否存在
                raise ValueError(f"LLM生成结果缺少text键，原始返回：{str(response['generations'][0][0])}")

            logger.info("回答生成完成")
            # 从字典中提取回答文本（适配字典结构）
            return response["generations"][0][0]["text"].strip()
        except Exception as e:
            logger.error(f"回答生成失败: {str(e)}", exc_info=True)
            raise RuntimeError(f"生成回答失败: {str(e)}")

    def query(self, question: str, top_k: int = 3) -> Dict[str, any]:
        """处理问答请求的主方法"""
        try:
            logger.info(f"开始处理问答请求：question={question[:50]}...，top_k={top_k}")
            related_docs = self._retrieve_relevant_docs(question, top_k)
            answer = self._generate_answer(question, related_docs)
            return {
                "answer": answer,
                "related_docs": related_docs,
                "success": True
            }
        except Exception as e:
            logger.error(f"问答请求处理失败: {str(e)}", exc_info=True)
            return {
                "answer": f"处理失败: {str(e)}",
                "related_docs": [],
                "success": False
            }
